/**
 * Copyright (c) 2004-2005, Regents of the University of California
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * Neither the name of the University of California, Los Angeles nor the
 * names of its contributors may be used to endorse or promote products
 * derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package cck.util;

import cck.text.StringUtil;
import cck.text.Terminal;

import java.io.File;

/**
 * The <code>Util</code> class contains several utilities relating to exceptions and errors
 * that are useful.
 *
 * @author Ben L. Titzer
 */
public class Util {
    /**
     * The <code>Error</code> class is the base class of errors that contains some
     * extra helper methods to generate a report. It provides a few extra utility
     * functions and is useful for distinguishing exceptions generated by the application
     * itself and the Java runtime.
     */
    public static class Error extends java.lang.Error {

        protected final String message, param;
        public static boolean STACKTRACES;

        public Error(String p) {
            super(p);
            message = "Error";
            param = p;
        }

        public Error(String n, String p) {
            super(n);
            message = n;
            param = p;
        }

        public String getParam() {
            return param;
        }

        /**
         * The <code>report()</code> method generates a textual report that is printed
         * on the terminal for the user.
         */
        public void report() {
            Terminal.print(Terminal.ERROR_COLOR, message);
            Terminal.print(": " + param + '\n');
            if (STACKTRACES) {
                printStackTrace();
            }
        }
    }

    /**
     * The <code>InternalError</code> class is a class of errors corresponding to exceptional conditions
     * within the application.
     */
    public static class InternalError extends Error {
        private String category;

        public InternalError(String param) {
            super(param);
            category = "Internal Error";
        }

        public InternalError(String c, String param) {
            super(param);
            category = c;
        }

        public void report() {
            Terminal.print(Terminal.ERROR_COLOR, category);
            Terminal.print(": " + param + '\n');
            printStackTrace();
        }
    }

    /**
     * The <code>Unexpected</code> class wraps an unexpected exception that may happen during
     * execution. This is useful for a "catch all" type of clause to handle all the possible
     * exceptions that could happen during execution without having to write explicit handlers
     * for each.
     */
    public static class Unexpected extends Error {
        public final Throwable thrown;

        public Unexpected(Throwable t) {
            super(StringUtil.quote(t.getClass()));
            thrown = t;
        }

        public void report() {
            Terminal.print(Terminal.ERROR_COLOR, "Unexpected exception");
            Terminal.print(": " + param + '\n');
            thrown.printStackTrace();
        }

        public void rethrow() throws Throwable {
            throw thrown;
        }
    }

    /**
     * The <code>unimplemented()</code> method is a utility that constructs a
     * <code>InternalError</code> instance. This is called from methods or classes with unimplemented
     * functionality for documentation and fail-fast purposes.
     *
     * @return an instance of the <code>Util.InternalError</code> class that specifies that this
     *         functionality is not yet implemented
     */
    public static InternalError unimplemented() {
        return new InternalError("unimplemented");
    }

    /**
     * The <code>unreachable()</code> method is a utility that constructs a
     * <code>InternalError</code> instance. This is called from states that are expected
     * to be unreachable.
     *
     * @return an instance of the <code>Util.InternalError</code> class that specifies that
     *         this case is unreachable
     */
    public static InternalError unreachable() {
        return new InternalError("unreachable");
    }

    /**
     * The <code>failure()</code> method is a utility that constructs a
     * <code>InternalError</code> instance with the specified message. It is useful for internal
     * error conditions and defensive programming.
     *
     * @param s the error message for the failure
     * @return an instance of the <code>Util.InternalError</code> class with the specified error message
     */
    public static InternalError failure(String s) {
        return new InternalError(s);
    }

    /**
     * The <code>enforce()</code> method is a utility similar to <code>assert</code>
     * that checks a boolean predicate at runtime, throwing the specified internal error
     * upon failure. It is useful for internal error conditions and defensive programming.
     *
     * @param b the boolean condition to enforce
     * @param s the string error message
     */
    public static void enforce(boolean b, String s) {
        if ( !b ) throw new InternalError(s);
    }


    /**
     * The <code>failure()</code> method is a utility that constructs a
     * <code>InternalError</code> instance with the specified message. It is useful for internal
     * error conditions and defensive programming.
     *
     * @param c the class of the error
     * @param s the error message
     * @return an instance of the <code>Util.InternalError</code> class with the specified error message
     */
    public static InternalError failure(String c, String s) {
        return new InternalError(c, s);
    }

    /**
     * The <code>warning()</code> method writes a warning to the terminal. It does not
     * produce an exception or a stack trace.
     *
     * @param s the warning to report
     */
    public static void warning(String s) {
        Terminal.print(Terminal.WARN_COLOR, "Internal Warning");
        Terminal.print(": " + s + '\n');
    }

    /**
     * The <code>unexpected()</code> method is a utility method that wraps an unexpected exception
     * so that it can be throw again and reported later. This is useful for code that does IO but does
     * not want to handle IO exceptions, for example.
     *
     * @param t the throwable that was encountered
     * @return a new instance of the <code>Unexpected</code> class that wraps up the thrown exception
     */
    public static Unexpected unexpected(Throwable t) {
        return new Unexpected(t);
    }

    /**
     * The <code>userError()</code> method constructs and throws an error in situations that are likely
     * due to user error. This is useful for files that are not found, an incorrect option value, etc.
     *
     * @param s the message for the user
     */
    public static void userError(String s) {
        throw new Error(s);
    }

    /**
     * The <code>userError()</code> method constructs and throws an error in situations that are likely
     * due to user error. This is useful for files that are not found, an incorrect option value, etc.
     *
     * @param s the message for the user
     * @param p the parameter to the message, automatically put in quotes
     */
    public static void userError(String s, String p) {
        throw new Error(s, p);
    }

    /**
     * The <code>verifyFilesExist()</code> method verifies that each of the specified
     * files exists and is readable. If one of the files does not exist, it will report
     * an error to the user.
     *
     * @param files the files to verify the existence of
     * @return true if all of the files exist and are readable
     */
    public static boolean verifyFilesExist(String[] files) {
        boolean success = true;
        for (int cntr = 0; cntr < files.length; cntr++) {
            String file = files[cntr];
            if (!verifyFileExists(file)) success = false;
        }
        return success;
    }

    /**
     * The <code>verifyFileExists()</code> method verifies that the specified file
     * exists and is readable. If the file does not exist, it will report an error
     * to the user and return <code>false</code>.
     *
     * @param file the name of the file
     * @return true if the file exists and is readable; false otherwise
     */
    public static boolean verifyFileExists(String file) {
        File f = new File(file);
        if (!f.exists()) {
            userError("File not found", file);
            return false;
        }
        return true;
    }


}
